package pikasial.graphique;

import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.ImageIcon;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import pikasial.fs.Dossier;
import pikasial.fs.PikasialFS;
import pikasial.images.ImageContainer;
import pikasial.images.ListeAlbums;

/**
 * Contient une vue du système de fichiers de Pikasial sous forme arborescente.
 * <p>
 * Cette classe observe le système de fichiers de Pikasial (c'est-à-dire les
 * dossiers/fichiers qui ont été importés par l'utilisateur),
 * et les affiche dans un JScrollPane sous la forme d'un arbre
 *(comme le fait un explorateur).
 * Elle permet également:
 * <ul>
 * <li> la sélection d'un dossier pour afficher les images qu'il contient
 * dans le navigateur de photos (sous forme de vignettes)
 * <li> la sélection d'un fichier pour afficher l'image correspondante dans
 * le navigateur de photos (taille réelle)
 * <li> l'apparition d'un menu contextuel pour modifier le système de fichiers
 * </ul>
 * L'arbre est mis à jour à chaque modification du système de fichiers.
 *
 * @author ESIAL - Groupe 14
 * @see pikasial.fs.PikasialFS
 */
@SuppressWarnings("serial")
public final class NavigateurDossiers extends JScrollPane
    implements Observer {

    /** L'arbre representant le système de fichier. */
    private JTree arbre;
    /** Le menu contextuel. */
    private JPopupMenu popup;
    /** Indique le noeud sélectionné dans l'arbre. */
    private TreePath selectedPath = null;

    /**
     * Crée une instance de NavigateurDossier.
     */
    public NavigateurDossiers() {

        pikasial.fs.PikasialFS.getInstance().addObserver(this);
        
        arbre = new JTree(pikasial.fs.PikasialFS.getInstance().getModel());
        arbre.setRootVisible(true);
        arbre.setShowsRootHandles(false);

        arbre.setCellRenderer(new ArbreCellRenderer());

        arbre.addMouseListener(new MouseListener() {

            public void mouseClicked(final MouseEvent arg0) {
            	if (arg0.getButton() == MouseEvent.BUTTON1) {
            		Dossier node = (Dossier) arbre.getLastSelectedPathComponent();
            		if (node != null) {
                            ListeAlbums.getInstance().setAlbumAffiche(node);
                        }
            	}
            }

            public void mouseEntered(final MouseEvent arg0) {}
            public void mouseExited(final MouseEvent arg0) {}
            public void mousePressed(final MouseEvent arg0) {}

            public void mouseReleased(final MouseEvent e) {
                if (e.getButton() == MouseEvent.BUTTON3) {
                    selectedPath = arbre.getPathForLocation(
                        e.getX(), e.getY());
                    if (selectedPath != null && 
                            !(((Dossier)selectedPath.getLastPathComponent()).equals(PikasialFS.getInstance().getModel().getRoot())) ) {
                        arbre.setSelectionPath(selectedPath);
                        popup.show(e.getComponent(), e.getX(), e.getY());
                    }
                }
            }
        });

        arbre.getSelectionModel().setSelectionMode(
            TreeSelectionModel.SINGLE_TREE_SELECTION);

        createPopupMenu();
        setViewportView(arbre);
    }

    /**
     * Retourne l'arbre.
     * @return l'arbre
     */
    JTree getArbre() {
        return arbre;
    }

    /**
     * Crée le menu contextuel qui sera affiché en cas de clic droit
     * sur un élement de l'arbre.
     * <p>
     * Le menu contextuel sera affiché plus tard lors d'un clic droit
     * en appelant la méthode <code>show()</code>
     * @see JPopupMenu
     */
    private void createPopupMenu() {
        popup = new JPopupMenu();

        JMenuItem delFromPika = new JMenuItem("Supprimer de Pikasial...");
        delFromPika.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
                if (selectedPath != null) {
                    ImageContainer actuel = pikasial.images.ListeAlbums.getInstance().getAlbumAffiche();

                    Dossier selectedDossier = (Dossier)
                        selectedPath.getLastPathComponent();

                    TreePath tp = PikasialFS.getInstance()
                            .delAndReturnParentPath(selectedDossier);
                    
                    if (actuel.equals(selectedDossier)
                        || ((Dossier)actuel).getJavaFile().getPath().startsWith(
            selectedDossier.getJavaFile().getPath())) {
                        arbre.setSelectionPath(tp);
                        arbre.expandPath(tp);
                        pikasial.images.ListeAlbums.getInstance()
                                .setAlbumAffiche((Dossier)tp.getLastPathComponent());
                    } else {
                        tp = PikasialFS.getInstance().findPath((Dossier) actuel);
                        arbre.setSelectionPath(tp);
                        arbre.expandPath(tp);
                        pikasial.images.ListeAlbums.getInstance()
                                .setAlbumAffiche(actuel);
                    }
                }
            }
        });
        popup.add(delFromPika);
        
        JMenuItem maj = new JMenuItem("Mettre à jour le dossier");
        maj.addActionListener(new ActionListener() {
            public void actionPerformed(final ActionEvent e) {
               if (selectedPath != null) {
                    ImageContainer act = pikasial.images.ListeAlbums.getInstance().getAlbumAffiche();
                    Dossier sd = (Dossier)
                        selectedPath.getLastPathComponent();
                    
                    PikasialFS.getInstance().delAndReturnParentPath(sd);
                    PikasialFS.getInstance().add(sd, false);

                    pikasial.images.ListeAlbums.getInstance().setAlbumAffiche(sd);
                    
               }
            }
        });
        popup.add(maj);
        
    }

    /**
     * Appelée quand l'objet observé (instance de PikasialFS) notifie
     * un changement.
     * <p>
     * Le but n'est pas de mettre à jour l'arbre, car JTree se met à jour tout
     * seul lorsque son modèle change. Il s'agit de sélectionner l'élement
     * qui a été ajouté au système de fichiers, et d'étendre l'arbre pour que
     * celui-ci soit visible.
     *
     * @param arg0 - l'objet observé qui a changé
     * @param arg1 - l'objet passé en paramètre par l'observé, ici c'est une
     * instance de <code>TreePath</code>
     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void update(final Observable arg0, final Object arg1) {
        if (arg1 instanceof TreePath) {
            SwingUtilities.invokeLater(new Runnable() {
                            public void run() {
            arbre.setSelectionPath((TreePath) arg1);
            arbre.expandPath((TreePath) arg1);
                            }
            });
        } 
    }
}

class ArbreCellRenderer extends DefaultTreeCellRenderer {

	private static final long serialVersionUID = -194549480428370116L;

	public ArbreCellRenderer() {
	}

	@Override
	public Component getTreeCellRendererComponent(
			JTree tree,
			Object value,
			boolean sel,
			boolean expanded,
			boolean leaf,
			int row,
			boolean hasFocus) {

		super.getTreeCellRendererComponent(
				tree, value, sel,
				expanded, leaf, row,
				hasFocus);

		setFont(new Font(null, Font.BOLD, 10));

		if (!tree.getModel().getRoot().equals(value)) {
                    
			setOpenIcon(new ImageIcon(getClass().getResource(pikasial.utils.PikasialConstantes.OPENED_FOLDER_16_ICON_PATH)));
			setClosedIcon(new ImageIcon(getClass().getResource(pikasial.utils.PikasialConstantes.FOLDER_16_ICON_PATH)));

		} else {
			ImageIcon icon = new ImageIcon(getClass().getResource("/img/gnome-dev-computer.png"));
			if (icon != null) {
				setIcon(icon);
			}
        }
        return this;
    }
}
